--***********************************************************
--**                    ROBERT JOHNSON                     **
--** Class wich help you to drag an item over the world, it display a ghost render of the item
--** You can either press R or let the left mouse btn down and drag the mouse around to rotate the item                     **
--***********************************************************


require "ISBaseObject"

ISBuildingObject = ISBaseObject:derive("ISBuildingObject");

local function predicateNotBroken(item)
	return not item:isBroken()
end

--************************************************************************--
--** ISBuildingObject:initialise
--**
--************************************************************************--
function ISBuildingObject:initialise()

end

--************************************************************************--
--** ISBuildingObject:new
--**
--************************************************************************--
--~ function ISBuildingObject:derive (type)
--~     local o = {}
--~     setmetatable(o, self)
--~     self.__index = self
--~ 	o.Type= type;
--~     return o
--~ end

function ISBuildingObject:setCanPassThrough(passThrough)
	self.canPassThrough = passThrough;
end

function ISBuildingObject:setNorthSprite(sprite)
	self.northSprite = sprite;
end

function ISBuildingObject:setEastSprite(sprite)
	self.eastSprite = sprite;
end

function ISBuildingObject:setSouthSprite(sprite)
	self.southSprite = sprite;
end

function ISBuildingObject:setSprite(sprite)
	self.sprite = sprite;
	self.choosenSprite = sprite;
end

function ISBuildingObject:setDragNilAfterPlace(nilAfter)
	self.dragNilAfterPlace = nilAfter;
end

function ISBuildingObject.onDestroy(thump, player)
	if thump:getContainer() and thump:getContainer():getItems() then
		local items = thump:getContainer():getItems()
		for i=0,items:size()-1 do
			thump:getSquare():AddWorldInventoryItem(items:get(i), 0.0, 0.0, 0.0)
		end
	end
	if camping.isTentObject(thump) then
		camping.destroyTent(thump)
		return
	end
	for index, value in pairs(thump:getModData()) do
		if luautils.stringStarts(index, "need:") then
			local itemFullType = luautils.split(index, ":")[2];
			for i=1,tonumber(value) do
				if ZombRand(2) == 0 then
					-- item destroyed
				elseif player then
                    player:getInventory():AddItem(itemFullType);
                else
                    thump:getSquare():AddWorldInventoryItem(itemFullType, 0.0, 0.0, 0.0);
                end
			end
		end
	end
--	ISBuildingObject.removeFromGround(thump:getSquare());
	local stairObjects = buildUtil.getStairObjects(thump)
	if #stairObjects > 0 then
		for i=1,#stairObjects do
			stairObjects[i]:getSquare():transmitRemoveItemFromSquare(stairObjects[i])
		end
    else
		thump:getSquare():transmitRemoveItemFromSquare(thump)
	end
end

function ISBuildingObject.removeFromGround(square)
	if square then
		for i = 0, square:getSpecialObjects():size() - 1 do
			local thump = square:getSpecialObjects():get(i);
			if instanceof(thump, "IsoThumpable") then
				square:transmitRemoveItemFromSquare(thump);
				break;
			end
		end
	end
end

local function isMouseOverUI()
	local uis = UIManager.getUI()
	for i=1,uis:size() do
		local ui = uis:get(i-1)
		if ui:isMouseOver() then
			return true
		end
	end
	return false
end

-- render the item on the ground or launch the build
function DoTileBuilding(draggingItem, isRender, x, y, z, square)
	local spriteName = nil;
	if not draggingItem.player then print('ERROR: player not set in DoTileBuilding'); draggingItem.player = 0 end;
	-- if the square is nil we have to create it (for example, the 2nd floor square are nil)
	if square == nil and getWorld():isValidSquare(x, y, z) then
--~ 		print("create new square : " .. x .. " " .. y);
		square = getCell():createNewGridSquare(x, y, z, true);
--~ 		print("square created : " .. newSq:getX() .. " " .. newSq:getY());
	end
--~ 	print("dragging : " .. x .. " " .. y);
--~ 	print("square is : " .. square:getX() .. " " .. square:getY());
	-- get the sprite we have to display
	if draggingItem.player == 0 and wasMouseActiveMoreRecentlyThanJoypad() then
		local mouseOverUI = isMouseOverUI();
		if Mouse:isLeftDown() then
			if not draggingItem.isLeftDown then
				draggingItem.clickedUI = mouseOverUI;
				draggingItem.isLeftDown = true;
			end
			if draggingItem.clickedUI then return end
			draggingItem:rotateMouse(x, y);
		else
			if draggingItem.isLeftDown then
				draggingItem.isLeftDown = false;
				draggingItem.build = draggingItem.canBeBuild and not mouseOverUI and not draggingItem.clickedUI;
				draggingItem.clickedUI = false;
			end
			if mouseOverUI then return end
		end
	end
	spriteName = draggingItem:getSprite();
	-- if we have the left mouse button down, we fix the item to the square we clicked
	-- so while we have the left button down, we can drag the mouse to change the direction of the item (like in the Sims..)
	if (draggingItem.isLeftDown or draggingItem.build) and draggingItem.square then
		square = draggingItem.square;
		x = square:getX();
		y = square:getY();
	else -- else, the square is the one our mouse is on
		draggingItem.square = square;
	end
	-- There may be no square if we are at the edge of the map.
	if not square then
		draggingItem.canBeBuild = false
		return
	end
	-- render our item on the ground, if it can be placed we render it with a bit of red over it
	if isRender then
		-- we first call the isValid function of our item
		draggingItem.canBeBuild = draggingItem:isValid(square, draggingItem.north)
		-- we call the render function of our item, because for stairs (for example), we drag only 1 item : the 1st part of the stairs
		-- so in the :render function is ISWoodenStair, we gonna display the 2 other part of the stairs, depending on his direction
		draggingItem:render(x, y, z, square)
	end
	-- finally build our item !
	if draggingItem.canBeBuild and draggingItem.build then
		draggingItem.build = false
		draggingItem:tryBuild(x, y, z)
	end
	if draggingItem.build and not draggingItem.dragNilAfterPlace then
		draggingItem:reinit();
	end
end

function ISBuildingObject:tryBuild(x, y, z)
	local square = getCell():getGridSquare(x, y, z)
	local playerObj = getSpecificPlayer(self.player)
	local playerInv = playerObj:getInventory()
	if ISBuildMenu.cheat or self:walkTo(x, y, z) or ((self.Type == "fishingNet") and (self:isValid(square, true))) then
		if self.dragNilAfterPlace then
			getCell():setDrag(nil, self.player)
		end
		-- if you give a custom maxTime, if not it's calculated with the carpentry level
		local maxTime = (200 - (playerObj:getPerkLevel(Perks.Woodwork) * 5))
		if self.maxTime then
			maxTime = self.maxTime
		end
		if playerObj:isTimedActionInstant() then
			maxTime = 1
		end
		if self.skipBuildAction then
			-- farmingPlot doesn't need another action
			self:create(x, y, z, self.north, self:getSprite())
		else
			if not self.noNeedHammer and not ISBuildMenu.cheat then
				local hammer = playerInv:getFirstTagEvalRecurse("Hammer", predicateNotBroken)
				if hammer then
					ISInventoryPaneContextMenu.equipWeapon(hammer, true, false, self.player)
				end
            end
            if not ISBuildMenu.cheat then
				if self.equipBothHandItem then
					if luautils.haveToBeTransfered(playerObj, self.equipBothHandItem) then
						ISTimedActionQueue.add(ISInventoryTransferAction:new(playerObj, self.equipBothHandItem, self.equipBothHandItem:getContainer(), playerInv));
					end
					ISInventoryPaneContextMenu.equipWeapon(self.equipBothHandItem, true, true, self.player)
				end
                if self.firstItem then
					local item = nil
					if self.firstPredicate then
						item = playerInv:getFirstTypeEvalArgRecurse(self.firstItem, self.firstPredicate, self.firstArg)
						if not item then
							local groundItems = buildUtil.getMaterialOnGround(square)
							for _,item2 in ipairs(groundItems[self.firstItem]) do
								if self.firstPredicate(item2, self.firstArg) then
									item = item2
									break
								end
							end
							local time = ISWorldObjectContextMenu.grabItemTime(playerObj, item:getWorldItem())
							ISTimedActionQueue.add(ISGrabItemAction:new(playerObj, item:getWorldItem(), time))
						end
					else
						item = playerInv:getItemFromType(self.firstItem, true, true)
						if not item then
							local groundItems = buildUtil.getMaterialOnGround(square)
							item = groundItems[self.firstItem][1]
							local time = ISWorldObjectContextMenu.grabItemTime(playerObj, item:getWorldItem())
							ISTimedActionQueue.add(ISGrabItemAction:new(playerObj, item:getWorldItem(), time))
						end
					end
					ISInventoryPaneContextMenu.equipWeapon(item, true, false, self.player)
                end
                if self.secondItem then
					local item = playerInv:getItemFromType(self.secondItem, true, true)
					if instanceof(item, "Clothing") then
						if not item:isEquipped() then
							ISInventoryPaneContextMenu.wearItem(item, self.player)
						end
					else
						ISInventoryPaneContextMenu.equipWeapon(item, false, false, self.player)
					end
                end
            end

			-- Pass a copy of this object to ISBuildAction to avoid issues with changing this object
			-- before the action completes, such as rotating it with the 'Rotate Building' key.
			local selfCopy = copyTable(self)
			setmetatable(selfCopy, getmetatable(self, true))

			ISTimedActionQueue.add(ISBuildAction:new(playerObj, selfCopy, x, y, z, self.north, self:getSprite(), maxTime))
		end
	end
end

-- Override this to change the position the player walks to.
function ISBuildingObject:walkTo(x, y, z)
	if self.skipWalk2 then
		return true;
	end
	local square = getCell():getGridSquare(x, y, z)
	local playerObj = getSpecificPlayer(self.player)
	if self.isWallLike then
		return luautils.walkAdjWall(playerObj, square, self.north)
	end
	return luautils.walkAdj(playerObj, square)
end

-- Called in ISBuildAction:start().
-- Override this to call setOverrideHandModels() and/or player:SetVariable().
function ISBuildingObject:onTimedActionStart(action)
	local actionAnim = CharacterActionAnims.Build
	if self.actionAnim then
		actionAnim = self.actionAnim
	elseif self.firstItem == "BlowTorch" then
		actionAnim = "BlowTorch"
		if self.buildLow then
			actionAnim = "BlowTorchFloor"
		end
	else
		if self.buildLow then
			actionAnim = CharacterActionAnims.BuildLow
		end
	end
	action:setActionAnim(actionAnim)

	if action.character:isPrimaryEquipped("Base.BlowTorch") then
		action:setOverrideHandModels(action.character:getPrimaryHandItem(), nil)
	end
end

-- Called in ISBuildAction:stop().
function ISBuildingObject:onTimedActionStop(action)
end

function ISBuildingObject:haveMaterial(square)
	if ISBuildMenu.cheat then
		return true;
	end
	local groundItems = buildUtil.getMaterialOnGround(square);
	local groundItemCounts = buildUtil.getMaterialOnGroundCounts(groundItems)
	local groundItemUses = buildUtil.getMaterialOnGroundUses(groundItems)
    local dragItem = self
	local modData = dragItem.modData;
	local playerObj = getSpecificPlayer(dragItem.player)
	local playerInv = playerObj:getInventory()
	if modData ~= nil then
		for index, value in pairs(modData) do
			if luautils.stringStarts(index, "need:") then
				local itemFullType = luautils.split(index, ":")[2];
				local nbOfItem = playerInv:getCountTypeEvalRecurse(itemFullType, buildUtil.predicateMaterial)
				-- if the build recipe requires nails, check the player's inventory for boxes of nails, and add them to the count
				if itemFullType == "Base.Nails" then
					nbOfItem = nbOfItem + playerInv:getCountTypeEvalRecurse("Base.NailsBox", buildUtil.predicateMaterial)*100;
					if groundItemCounts["Base.NailsBox"] then
						nbOfItem = nbOfItem + groundItemCounts["Base.NailsBox"]*100;
					end
				end
				if groundItemCounts[itemFullType] then
					nbOfItem = nbOfItem + groundItemCounts[itemFullType];
				end
				if nbOfItem < tonumber(value) then
					return false;
				end
			end
			if luautils.stringStarts(index, "use:") then
				local itemFullType = luautils.split(index, ":")[2];
				local nbOfUse = playerInv:getUsesTypeRecurse(itemFullType)
				if groundItemUses[itemFullType] then
					nbOfUse = nbOfUse + groundItemUses[itemFullType];
				end
				if nbOfUse < tonumber(value) then
					return false;
				end
			end
		end
	end
	
	if not self.noNeedHammer and not ISBuildMenu.cheat then
		local hammer = playerInv:getFirstTagEvalRecurse("Hammer", predicateNotBroken);
		if not hammer then
			return false;
		end
	end
	return true;
end

function ISBuildingObject:reinit()
--~ 	ISBuildingObject.nSprite = 1;
	self.isLeftDown = false;
	self.clickedUI = false;
	self.canBeBuild = false;
	self.build = false;
	self.square = nil;
--~ 	ISBuildingObject.north = false;
end

function ISBuildingObject:reset()
--	getCell():setDrag(nil);
	self.northSprite = nil;
	self.sprite = nil;
	self.southSprite = nil;
	self.eastSprite = nil;
	self.nSprite = 1;
	self.isLeftDown = false;
	self.clickedUI = false;
	self.canBeBuild = false;
	self.build = false;
	self.square = nil;
	self.north = false;
	self.south = false;
	self.east = false;
	self.west = false;
	self.choosenSprite = nil;
	self.dragNilAfterPlace = false;
	self.xJoypad = -1;
	self.yJoypad = -1;
	self.zJoypad = -1;
	self.isWallLike = false;
	self.isCorner = false;
	self.completionSound = nil;
end

function ISBuildingObject:init()
	self:reset();
	self.canBeAlwaysPlaced = false;
	self.isContainer = false;
	self.canPassThrough = false;
	self.canBarricade = false;
	self.thumpDmg = 8;
	self.isDoor = false;
	self.isDoorFrame = false;
	self.crossSpeed = 1.0;
	self.blockAllTheSquare = false;
	self.dismantable = false;
	self.canBePlastered = false;
	self.hoppable = false;
    self.isThumpable = true;
	self.modData = {};
end

-- get the sprite depending on the position of the mouse and if the player press "r" or not
function ISBuildingObject:getSprite()
	self.north = false;
	self.south = false;
	self.east = false;
	self.west = false;
	self.choosenSprite = self.sprite;
	if self.nSprite == 1 then
		self.west = true;
		self.choosenSprite = self.sprite;
	elseif self.nSprite == 2 then
		self.north = true;
		self.choosenSprite = self.northSprite;
	elseif self.nSprite == 3 then
		if self.eastSprite then
			self.choosenSprite = self.eastSprite;
			self.east = true;
		else
			self.west = true;
			self.choosenSprite = self.sprite;
		end
	elseif self.nSprite == 4 then
		if self.southSprite then
			self.south = true;
			self.choosenSprite = self.southSprite;
		else
			self.north = true;
			self.choosenSprite = self.northSprite;
		end
	end
	return self.choosenSprite;
end

function ISBuildingObject:isValid(square)
    if self.notExterior and not square:Is(IsoFlagType.exterior) then return false end
	if not self:haveMaterial(square) then return false end
	if square:isVehicleIntersecting() then return false end
	if self.canBeAlwaysPlaced then
		-- even if we can place this item everywhere, we can't place 2 same objects on the same tile
		for i=0,square:getObjects():size()-1 do
			local obj = square:getObjects():get(i);
			if self:getSprite() == obj:getTextureName() then
				return false
			end
		end
		return true
	end
	local blockedByCharacters = self.isWallLike ~= true
	return buildUtil.canBePlace(self, square) and square:isFreeOrMidair(blockedByCharacters)
end

function ISBuildingObject:render(x, y, z, square)
	-- ghost sprite is a blueprint of what we're currently building
	if self.ghostSprite then
		self.ghostSprite:RenderGhostTile(self.ghostSpriteX, self.ghostSpriteY, self.ghostSpriteZ);
		return;
	end
	-- optionally draw a floor tile to aid placement (stacked wooden crates for example)
	if self.renderFloorHelper then
		if not self.RENDER_SPRITE_FLOOR then
			self.RENDER_SPRITE_FLOOR = IsoSprite.new()
			self.RENDER_SPRITE_FLOOR:LoadFramesNoDirPageSimple('carpentry_02_56')
		end
		self.RENDER_SPRITE_FLOOR:RenderGhostTile(x, y, z)
	end

	local spriteName = self:getSprite()
	if not self.RENDER_SPRITE then
		self.RENDER_SPRITE = IsoSprite.new()
	end
	if self.RENDER_SPRITE_NAME ~= spriteName then
		self.RENDER_SPRITE:LoadFramesNoDirPageSimple(spriteName)
		self.RENDER_SPRITE_NAME = spriteName
	end

	local sharedSprite = getSprite(self.RENDER_SPRITE_NAME)
	if square and sharedSprite and sharedSprite:getProperties():Is("IsStackable") then
		local props = ISMoveableSpriteProps.new(self.RENDER_SPRITE)
		local offsetY = props:getTotalTableHeight(square)
		local r,g,b,a = 1,1,1,0.6
		if not self:isValid(square) then
			r,g,b,a = 0.65,0.2,0.2,0.6
		end
		self.RENDER_SPRITE:RenderGhostTileColor(x, y, z, 0, offsetY * Core.getTileScale(), r, g, b, a)
		return
	end

	-- if the square is free and our item can be build
	if self:isValid(square) then
		self.RENDER_SPRITE:RenderGhostTile(x, y, z);
	else
		self.RENDER_SPRITE:RenderGhostTileRed(x, y, z);
	end
end

function ISBuildingObject:rotateKey(key)
	if key == getCore():getKey("Rotate building") then
		self.nSprite = self.nSprite + 1;
		if self.nSprite > 4 then
			self.nSprite = 1;
		end
	end
end

local function rotateKey(key)
	if getCell() and getCell():getDrag(0) then
		getCell():getDrag(0):rotateKey(key)
	end
end

-- we gonna rotate our building depending on the position of the mouse
function ISBuildingObject:rotateMouse(x, y)
	if self.square then
		-- we start to get the direction the mouse is compared to the selected square for the item
		local difx = x - self.square:getX();
		local dify = y - self.square:getY();
		-- west
		if difx < 0 and math.abs(difx) > math.abs(dify) then
			self.nSprite = 1;
		end
		-- east
		if difx > 0 and math.abs(difx) > math.abs(dify) then
			self.nSprite = 3;
		end
		-- north
		if dify < 0 and math.abs(difx) < math.abs(dify) then
			self.nSprite = 2;
		end
		-- south
		if dify > 0 and math.abs(difx) < math.abs(dify) then
			self.nSprite = 4;
		end
	end
end

-- Called by Java in IsoCell.setDrag().  Can be overridden to do stuff.
function ISBuildingObject:deactivate()
end

function ISBuildingObject:onJoypadPressButton(joypadIndex, joypadData, button)
    local playerObj = getSpecificPlayer(joypadData.player)
    if button == Joypad.AButton then
        if self.canBeBuild then
            self:tryBuild(self.xJoypad, self.yJoypad, self.zJoypad)
        end
    end

    if button == Joypad.BButton then
        getCell():setDrag(nil, joypadData.player);
    end

    if button == Joypad.YButton then
        if self.isYButtonResetCursor then
            self.xJoypad = self.character:getCurrentSquare():getX()
            self.yJoypad = self.character:getCurrentSquare():getY()
        end
    end

    if button == Joypad.RBumper then
        self.nSprite = self.nSprite + 1;
        if self.nSprite > 4 then
            self.nSprite = 1;
        end
    end

    if button == Joypad.LBumper then
        self.nSprite = self.nSprite - 1;
        if self.nSprite < 1 then
            self.nSprite = 4;
        end
    end
end

function ISBuildingObject:onJoypadDirDown(joypadData)
    self.yJoypad = self.yJoypad + 1;
end

function ISBuildingObject:onJoypadDirUp(joypadData)
    self.yJoypad = self.yJoypad - 1;
end

function ISBuildingObject:onJoypadDirRight(joypadData)
    self.xJoypad = self.xJoypad + 1;
end

function ISBuildingObject:onJoypadDirLeft(joypadData)
    self.xJoypad = self.xJoypad - 1;
end

function ISBuildingObject:getAPrompt()
    if self.canBeBuild then
        return getText("ContextMenu_Build")
    end
    return nil
end

function ISBuildingObject:getBPrompt()
    return getText("UI_Cancel")
end

function ISBuildingObject:getYPrompt()
	if self.isYButtonResetCursor then
		return getText("IGUI_SetCursorToPlayerLocation")
	end
	return nil
end

function ISBuildingObject:getLBPrompt()
    return getText("IGUI_Controller_RotateLeft")
end

function ISBuildingObject:getRBPrompt()
    return getText("IGUI_Controller_RotateRight")
end

function DoTileBuildingJoyPad(draggingItem, isRender, x, y, z)
    if draggingItem.xJoypad == -1 then
        draggingItem.xJoypad = x;
        draggingItem.yJoypad = y;
--        local buts = getButtonPrompts(playerIndex);
--        if buts ~= nil then
--            buts:getBestLBButtonAction(nil);
--            buts:getBestRBButtonAction(nil);
--        end
    end
    draggingItem.zJoypad = z;
    local square = getCell():getGridSquare(draggingItem.xJoypad, draggingItem.yJoypad, draggingItem.zJoypad);
    DoTileBuilding(draggingItem, isRender, draggingItem.xJoypad, draggingItem.yJoypad, draggingItem.zJoypad, square);
end

Events.OnDoTileBuilding2.Add(DoTileBuilding);

Events.OnDoTileBuilding3.Add(DoTileBuildingJoyPad);
Events.OnKeyPressed.Add(rotateKey);

Events.OnDestroyIsoThumpable.Add(ISBuildingObject.onDestroy);
